/*!
 * \file kernel.h

 This file holds kernel wide macros and declarations.

 */

#ifndef _KERNEL_H_
#define _KERNEL_H_

#include <sysdefines.h>
#include "ipc.h"
#include "macros.h"
//#include "micro_libc.h"


/* Type definitions */

/* Macros */

#define SYSCALL_ARGUMENTS (thread_table[get_current_thread()]. \
                           data.registers.integer_registers)
/*!< Macro used in the system call switch to access the arguments to the
	 system call. */

#define KERNEL_VERSION          (0x0000000100000000)
/*!< Kernel version number. */

#define MAX_NUMBER_OF_PROCESSES (16)
/*!< Size of the process_table. */
#define MAX_NUMBER_OF_THREADS   (256)
/*!< Size of the thread_table. */
#define MAX_NUMBER_OF_CPUS      (16)
/*!< Size of the cpu_table and the maximal number of CPUs in the system. */
#define MAX_GLOBAl_SYSTEM_INTERRUPTS (64)
/*!< The number of ACPI Global System Interrupts supported by the kernel. The
	 range of interrupts is between 0..MAX_GLOBAL_SYSTEM_INTERRUPTS-1. */


/* Type declarations */

/*! Defines an execution context. */
struct context {
	char fpu_context[512];

	/*!< Stores the fpu/mmx/sse registers */
	struct {
		long rax;
		long rbx;
		long rcx;
		long rdx;
		long rdi;
		long rsi;
		long rbp;
		long rsp;
		long r8;
		long r9;
		long r10;
		long r11;
		long r12;
		long r13;
		long r14;
		long r15;
		long rflags; /*!< status flags */
		long rip; /*!< instruction pointer */
	} integer_registers;
	/*!< Stores all user visible integer registers. The segment registers are not
		 stored as the user mode processes can only use a code and a data segment
		 descriptor. We assume that the cs is set to the code and all the rest
		 of the segment registers are set to the data segment all the time. */

	long error_code;
	/*!< Holds the error code for an exception. */

	char from_interrupt;
	/*!< Is set to 1 if the context is saved during the processing of interrupts.
		 Set to 0 otherwise. */
};

/*! Defines a thread. */
union thread {

	struct {
		struct context registers; /*!< The context of the thread. Note: the
                                     context of the thread could include more
                                     than the accessible registers. */
		int owner; /*!< An index, into the process table, that
                                     identifies the process that owns this
                                     thread. */
		int next; /*!< This is an index into the thread_table.
                                     The index corresponds to the thread
                                     following this thread in a linked list.
                                     A thread can be in a number of linked
                                     lists. */
		unsigned long list_data; /*!< This member variable has different
                                     meaning depending on what list the thread
                                     resides in. In the timer queue this
                                     variable is either an absolute time or a
                                     delta time.*/
                unsigned long start_stack; /*!< This member variable is the start
                                            * address of the stack, this is used
                                            * by the terminate call to clean up
                                            * after the thread. */
	} data;
	char padding[1024];
};

/*! Defines a process. */
struct process {
	int threads; /*!< The number of threads running in this
                                     process. */
	int parent; /*!< This is an index into process_table. The
                                     index corresponds to the parent process.
                                 */
	unsigned long page_table_root; /*!< Address of the page table tree. */
};

/*! Defines the structure pointed to by the kernel GS_BASE. Every CPU has one
	of these. */
struct CPU_private {
	/* Must use long long below since the declaration is used in 32 and 64-bit
	   mode. */
	unsigned long long scratch_space; /*!< Temporary storage used during
                                           context switches. */
	unsigned long long stack; /*!< The kernel stack used by the CPU
                                           when executing system calls. */
	unsigned long long page_table_root; /*!< The root of the page table in use
                                           on the CPU. */
	int thread_index; /*!< Index into thread_table of the
                                           thread executing on the CPU. The
                                           idle thread has index -1. */
	int CPU_index; /*!< Index for this CPU. */
	unsigned int apic_phys_address; /*!< The physical address where the
                                             local apic registers are located.
                                         */
	int ticks_left_of_time_slice;
	/*!< Can be used by a preemptive
		 scheduler. */

	unsigned int local_apic_id; /*!< The id of the local APIC connected
                                           to the CPU. */
	unsigned int pad; /*!< Dummy member used to make sure the
                                           struct looks the same way in both 32
                                           and 64-bit modes. */
};

/*! Holds information on a I/O APIC. */
struct IO_APIC {
	unsigned int ioapic_phys_address; /*!< The physical address where the
                                               io apic registers are located.
                                           */
	int local_apic_id; /*!< The id of the io apic. */
};

/* ELF image structures. The names from the ELF64 specification are used and
   the structs are derived from the ELF64 specification. */

#define EI_MAG0       0  /*!< The first four bytes in an ELF image is
                              0x7f 'E' 'L' 'F'. */
#define EI_MAG1       1
#define EI_MAG2       2
#define EI_MAG3       3
#define EI_CLASS      4  /*!< The specific ELF image class. */
#define EI_DATA       5  /*!< Describes if the image is big- or little-
                              endian. */
#define EI_VERSION    6  /*!< The version of the ELF specification the
                              image adheres to. */
#define EI_OSABI      7  /*!< Type of OS the image can be run on. */
#define EI_ABIVERSION 8  /*!< Version of the ABI used. */
#define EI_PAD        9  /*!< First unused byte in the identification array.
                          */
#define EI_NIDENT     16 /*!< Number of entries in the identification array */

/*! Defines an ELF64 file header. */
struct Elf64_Ehdr {
	unsigned char e_ident[EI_NIDENT]; /*!< Array of bytes that shows that this
                                        is an ELF image and what type of ELF
                                        image it is. */
	short e_type; /*!< The type of ELF executable image. */
	short e_machine; /*!< Identifies the type of machine that
                                        the image can execute on. */
	int e_version; /*!< The version of the ELF specification the
                                        image adheres to. */
	long e_entry; /*!< Start address of the executable. */
	long e_phoff; /*!< The offset into the image where the
                                        program header table is found. */
	long e_shoff; /*!< The offset into the image where the
                                        program header table is found. */
	int e_flags; /*!< Flags that are machine specific.
                                        These can be used to differentiate
                                        between similar machines. */
	short e_ehsize; /*!< The size, in bytes, of the header. */
	short e_phentsize; /*!< The size, in bytes, of each entry in
                                        the program header table. */
	short e_phnum; /*!< The size, in entries, of the program
                                        header table. */
	short e_shentsize; /*!< The size, in bytes, of each entry in
                                        the section header table. */
	short e_shnum; /*!< The size, in entries, of the section
                                        header table. */
	short e_shstrndx; /*!< The index, into the section table,
                                        of the section name string table.*/
};

/*! Defines an ELF32 file header. This is used for the kernel image itself. */
struct Elf32_Ehdr {
	unsigned char e_ident[EI_NIDENT]; /*!< Array of bytes that shows that this
                                        is an ELF image and what type of ELF
                                        image it is. */
	short e_type; /*!< The type of ELF executable image. */
	short e_machine; /*!< Identifies the type of machine that
                                        the image can execute on. */
	int e_version; /*!< The version of the ELF specification the
                                        image adheres to. */
	int e_entry; /*!< Start address of the executable. */
	int e_phoff; /*!< The offset into the image where the
                                        program header table is found. */
	int e_shoff; /*!< The offset into the image where the
                                        program header table is found. */
	int e_flags; /*!< Flags that are machine specific.
                                        These can be used to differentiate
                                        between similar machines. */
	short e_ehsize; /*!< The size, in bytes, of the header. */
	short e_phentsize; /*!< The size, in bytes, of each entry in
                                        the program header table. */
	short e_phnum; /*!< The size, in entries, of the program
                                        header table. */
	short e_shentsize; /*!< The size, in bytes, of each entry in
                                        the section header table. */
	short e_shnum; /*!< The size, in entries, of the section
                                        header table. */
	short e_shstrndx; /*!< The index, into the section table,
                                        of the section name string table.*/
};

/*! Defines an entry in the ELF program header table. Each entry corresponds
	to a segment. */
struct Elf64_Phdr {
	int p_type; /*!< Segments can have several types. p_type holds the type. */
	int p_flags; /*!< The attribute flags of the segment. */
	long p_offset; /*!< Offset into the image of the first byte of the
                     segment */
	long p_vaddr; /*!< The (virtual) address to which the segment is to be
                     loaded. */
	long p_paddr; /*!< Not used. */
	long p_filesz; /*!< The number of bytes the segment occupies in the
                     image. */
	long p_memsz; /*!< The number of bytes the segment occupies in memory. */
	long p_align; /*!< The alignment the segment should have in memory. This
                     field is currently being ignored. */
};

/*! Defines an entry in the ELF32 program header table. Each entry corresponds
	to a segment. */
struct Elf32_Phdr {
	int p_type; /*!< Segments can have several types. p_type holds the type. */
	int p_offset; /*!< Offset into the image of the first byte of the
                     segment */
	int p_vaddr; /*!< The (virtual) address to which the segment is to be
                     loaded. */
	int p_paddr; /*!< Not used. */
	int p_filesz; /*!< The number of bytes the segment occupies in the
                     image. */
	int p_memsz; /*!< The number of bytes the segment occupies in memory. */
	int p_flags; /*!< The attribute flags of the segment. */
	int p_align; /*!< The alignment the segment should have in memory. This
                     field is currently being ignored. */
};

/* Values used in p_type */
#define PT_NULL 0 /*!< The entry is not used. */
#define PT_LOAD 1 /*!< The segment can be loaded into memory. */
#define PT_PHDR 6 /*!< The segment only hold a program header table. */
/* Values used in p_flags */
#define PF_X        0x1        /*!< Segment can be executed.*/
#define PF_W        0x2        /*!< Segment can be written. */
#define PF_R        0x4        /*!< Segment can be read. */
#define PF_MASKPERM 0x0000FFFF /*!< Used to mask the permission bits */

/* Data structures describing the executable images embedded in the kernel
   image. */

/*! Defines an executable program. */
struct executable {
	const struct Elf64_Ehdr* elf_image; /*!< The start of the ELF
                                                      file header. */
	unsigned long memory_footprint_size; /*!< Size in bytes of the
                                                      program's memory foot
                                                      print when loaded. */
};

struct screen_position {
	unsigned char character; /*!< The character part of the byte tuple used for
                               each screen position. */
	unsigned char attribute; /*!< The character part of the byte tuple used for
                               each screen position. */
};

/*!< Defines a VGA text mode screen position. */

struct screen {
	struct screen_position positions[25][80];
};
/*!< Defines a VGA text mode screen. */

extern volatile unsigned int
bochs2_lock;

/* Variable declarations */

extern volatile unsigned int
port_table_lock;
/*!< Spin lock used to ensure mutual exclusion to the port table. */

extern volatile unsigned int
semaphore_table_lock;
/*!< Spin lock used to ensure mutual exclusion to the semaphore table. */

extern struct screen * const
screen_pointer;
/*!< Points to the VGA screen. */

extern volatile unsigned int
screen_lock;
/*!< Spin lock used to ensure mutual exclusion to the screen. */

extern union thread
thread_table[MAX_NUMBER_OF_THREADS];
/*!< Array holding all threads in the systems. */

extern volatile unsigned int
thread_table_lock;
/*!< Spin lock used to ensure mutual exclusion to the thread table. */

extern struct process
process_table[MAX_NUMBER_OF_PROCESSES];
/*!< Array holding all processes in the system. */

extern volatile unsigned int
process_table_lock;
/*!< Spin lock used to ensure mutual exclusion to the process table. */

extern struct CPU_private
CPU_private_table[MAX_NUMBER_OF_CPUS];
/*!< Array holding all the data structures private to the CPUs in the system.
 */

extern int
number_of_available_CPUs;
/*!< The number of available CPUs in the system. */

extern volatile int
number_of_initialized_CPUs;
/*!< The number of initialized CPUs in the system. */

extern volatile unsigned int
CPU_private_table_lock;
/*!< Spin lock used to ensure mutual exclusion to CPU_private_table. */

extern const struct Elf64_Ehdr * const
ELF_images[];
/*!< Array of pointers to executable images. */

extern struct thread_queue
ready_queue;
/*!< The ready queue. */

extern volatile unsigned int
ready_queue_lock;

extern volatile unsigned int
page_frame_table_lock;

extern int
timer_queue_head;
/*!< The index, into thread_table, of the head of the timer queue. The timer
	 queue is a list of threads blocked waiting for the system clock to reach
	 a certain time.  This variable is set to -1 if there are no threads in
	 the timer queue. */

extern volatile unsigned int
timer_queue_lock;
/*!< Spin lock used to ensure mutual exclusion to the timer queue. */

extern volatile long
system_time;
/*!< This variable holds the current system time. The system time is the
	 number of clock  ticks since system start. There are 200 clock ticks
	 per second. */

extern struct IO_APIC
IO_APIC;
/*!< Holds information on the I/O APIC. */

extern unsigned int
pic_interrupt_map[16];
/*!< This array maps 8259 interrupts to ACPI Global System Interrupts. */

extern const unsigned char
BSP_APIC_id;
/*!< The APIC id of the boostrap processor. Set by the assembly code. */

extern void*
AP_boot_stack;
/*!< This variable is used to pass the inital stack pointer value for the
	 application processor to the bootstrap code for the application
	 processor. */

extern unsigned int
GS_base;
/*!< This variable is used to pass the GS base value for the
	 application processor to the bootstrap code for the application
	 processor. */

extern unsigned int
TSS_selector;
/*!< This variable is used to pass the TSS selector value for the
	 application processor to the bootstrap code for the application
	 processor. */

extern const char start_application_processor[1];
/*!< The start of the code that initializes application processors. */

extern const char start_application_processor_end[1];
/*!< The end of the code that initializes application processors. */

/* Function declarations */

void
send_IPI(register unsigned char const destination_processor_index,
		register unsigned int const vector);

extern void
initialize_APIC(void);

/*! This fuction initializes the kernel after the assembly code portion has
	set the system and the CPU up. */
extern void
initialize(void);

/*! This function gets called from the syscall handler and responds to the
	system calls. */
extern void
system_call_handler(void);

/*! This function gets called from the interrupt handler and dispatches 
	interrupts to interrupt handlers. */
extern void
interrupt_dispatcher(const unsigned long interrupt_number /*!< The number of 
                                                               the interrupt 
                                                               vector.*/);

/*! This function gets called from the interrupt dispatcher and manages timer
	interrupts. */
extern void
timer_interrupt_handler(void);

/*! Outputs a string to the bochs console. */
extern void
kprints(const char* const string
		/*!< Points to a null terminated string */
		);

/*! Prints a long formatted as a hexadecimal number to the bochs console. */
extern void
kprinthex(const register long value
		/*!< The value to be written */);


/*! Clears the VGA buffer which is used in assignment 6. */
extern void
clear_screen(void);

inline int
allocate_thread(void);


/* Function definitions. */

/* The outb and outw functions are used when accessing hardware devices. */

/*! Wrapper for a byte out instruction. */
inline static void
outb(const short port_number, const char output_value) {
	__asm volatile("outb %%al,%%dx" : : "d" (port_number), "a" (output_value));
}

/*! Wrapper for a word out instruction. */
inline static void
outw(const short port_number, const short output_value) {
	__asm volatile("outw %%ax,%%dx" : : "d" (port_number), "a" (output_value));
}

/*! Wrapper for a byte in instruction. */
inline static char
inb(const short port_number) {
	char return_value;
	__asm volatile("inb %%dx,%%al" : "=a" (return_value) : "d" (port_number));
	return return_value;
}

/*! Wrapper for a word in instruction. */
inline static short
inw(const short port_number) {
	short return_value;
	__asm volatile("inw %%dx,%%ax" : "=a" (return_value) : "d" (port_number));
	return return_value;
}

/*! Get the thread currently executing on the CPU.
  \returns The index into the thread_table for the current thread. The function
		   returns -1 if the CPU is not executing any thread. */
inline static const int
get_current_thread(void) {
	register int current_thread;

	__asm volatile ("mov %%gs:24,%0" : "=r"(current_thread));

	return current_thread;
}

/*! Get the index for the CPU.
  \returns The index into the CPU_private_table for the current CPU. */
inline static const int
get_processor_index(void) {
	register int current_processor;

	__asm volatile ("mov %%gs:28,%0" : "=r"(current_processor));

	return current_processor;
}

/*! Wrapper for a 32-bit locked cmpxchg instruction.
	\returns The value stored in the variable pointed to by
	pointer_to_variable before the operation is performed. */
inline static unsigned int
lock_cmpxchg(register volatile unsigned int * const pointer_to_variable
		/*!< Pointer to the variable to
			 operate on. */,
		register unsigned int old_value
		/*!< The value assumed to be in
			 the variable. */,
		register const unsigned int new_value
		/*!< The value to conditionally
			 write to the variable. */) {
	__asm volatile("lock cmpxchgl %k1,%2"
			: "=a" (old_value)
			: "r"(new_value), "m" (*pointer_to_variable), "0" (old_value)
			: "memory");

	return old_value;
}

/*! Grabs a spin lock with write permissions */
inline static void
grab_lock_rw2(register volatile unsigned int * const spin_lock
		/*!< Points to the spin lock. */) {
	
	while (1) {
		while (*spin_lock != 0);

		if (0 == lock_cmpxchg(spin_lock, 0, 0x80000000)) {
			return;
		}
	}
}

/*! Grabs a spin lock with read permissions. */
inline static void
grab_lock_r2(register volatile unsigned int * const spin_lock
		/*!< Points to the spin lock. */) {

	while (1) {
		register unsigned int spin_lock_value, new_spin_lock_value;

		while (((*spin_lock) & 0x80000000) != 0);

		new_spin_lock_value = *spin_lock;

		while (1) {
			
			spin_lock_value = new_spin_lock_value;

			if (spin_lock_value > 0x7ffffffe)
				break;

			new_spin_lock_value = lock_cmpxchg(spin_lock,
					spin_lock_value,
					spin_lock_value + 1);
			if (new_spin_lock_value == spin_lock_value) {
				return;
			}
		}
	}
}

/*! Releases a spin lock. */
inline static void
release_lock2(register volatile unsigned int * const spin_lock
		/*!< Points to the spin lock. */) 
{
	register unsigned int spin_lock_value, new_spin_lock_value;

	new_spin_lock_value = *spin_lock;

	while (1) {
		spin_lock_value = new_spin_lock_value;

		if (0x80000000 == spin_lock_value)
			new_spin_lock_value = 0;
		else
			new_spin_lock_value = spin_lock_value - 1;

		while (0 != (new_spin_lock_value & 0x80000000))
			kprints("Kernel panic! Corrupt lock state.\n\0");

		new_spin_lock_value = lock_cmpxchg(spin_lock,
				spin_lock_value,
				new_spin_lock_value);
		if (new_spin_lock_value == spin_lock_value) {
			return;
		}
	}
}


inline static void
kprints_bochs(const char* string)
{
    grab_lock_rw2(&bochs2_lock);

//	outb(0xe9, get_processor_index()+'0');	// prefix with CPU-number

    // Loop until we have found the null character. 
    while (1) {
            register const char curr = *string++;

            if (curr) {
                    outb(0xe9, curr);
            } else {
                    release_lock2(&bochs2_lock);			
                    return;
            }
    }
    release_lock2(&bochs2_lock);
}

inline static void
kprinthex_bochs(const register long value)
{
	const static char hex_helper[16] = "0123456789abcdef";
	register int i;
	grab_lock_rw2(&bochs2_lock);

	// Print each character of the hexadecimal number. This is a very inefficient
	//   way of printing hexadecimal numbers. It is, however, very compact in terms
	//   of the number of source code lines. 
	for (i = 15; i >= 0; i--) {
		outb(0xe9, hex_helper[(value >> (i * 4))&15]);
	}
	release_lock2(&bochs2_lock);
}





/*! Grabs a spin lock with write permissions */
inline static void
grab_lock_rw(
	register volatile unsigned int * const spin_lock,
	const char *place
) 
{
	while (1) {
		kprints_bochs(place);

		while (*spin_lock != 0) {
			kprints_bochs(place);
		}

		if (0 == lock_cmpxchg(spin_lock, 0, 0x80000000)) {
			return;
		}
	}
}

/*! Grabs a spin lock with read permissions. */
inline static void
grab_lock_r(
	register volatile unsigned int * const spin_lock,
	const char *place
) 
{
	while (1) {
		kprints_bochs(place);
		register unsigned int spin_lock_value, new_spin_lock_value;

		while (((*spin_lock) & 0x80000000) != 0) {
			kprints_bochs(place);
		}

		new_spin_lock_value = *spin_lock;

		while (1) {
			kprints_bochs(place);

			spin_lock_value = new_spin_lock_value;

			if (spin_lock_value > 0x7ffffffe)
				break;

			new_spin_lock_value = lock_cmpxchg(spin_lock, spin_lock_value, spin_lock_value + 1);
			if (new_spin_lock_value == spin_lock_value) {
				return;
			}
		}
	}
}

/*! Releases a spin lock. */
inline static void
release_lock(
	register volatile unsigned int * const spin_lock,
	const char *place
) 
{
	register unsigned int spin_lock_value, new_spin_lock_value;

	new_spin_lock_value = *spin_lock;

	while (1) {
		kprints_bochs(place);
		spin_lock_value = new_spin_lock_value;

		if (0x80000000 == spin_lock_value)
			new_spin_lock_value = 0;
		else
			new_spin_lock_value = spin_lock_value - 1;

		while (0 != (new_spin_lock_value & 0x80000000)) {
			kprints("Kernel panic! Corrupt lock state.\n\0");
		}

		new_spin_lock_value = lock_cmpxchg(spin_lock, spin_lock_value, new_spin_lock_value);
		if (new_spin_lock_value == spin_lock_value) {
			return;
		}
	}
}





/*! Wrapper for reading the cr2 register.
  \returns The value in the cr2 register. */
inline static unsigned long
read_cr2(void) {
	register unsigned long return_value;
	__asm volatile("movq %%cr2,%0" : "=r" (return_value));
	return return_value;
}

/*! Invokes the bochs debugger */
inline static void
debugger(void) {
	outw(0x8a00, 0x8a00);
	outw(0x8a00, 0x8ae0);
}

#endif
